/*
 * Change Logs:
 * Date           Author       Notes
 * 2022-03-27     chenbin
 */
#include "fdown.h"
#if (FBOOT_USING_FDOWN == 1)

#define DBG_ENABLE
#define DBG_SECTION_NAME "fdown"
#define DBG_LEVEL DBG_INFO
#include <rtdbg.h>


#if (FDOWN_USING_YMODEM == 1)

#include <rtthread.h>
#include <stdio.h>
#include <stdbool.h>
#include <fal.h>
#include <ymodem.h>


struct fdown_ym2fal_ctx
{
	struct rym_ctx      parent;
	
	rt_device_t         device;
	
	size_t              update_file_size;
	size_t              update_file_pos;
	
	size_t              fal_part_offset;
	size_t              fal_part_max;
	
	uint32_t            tick_start;
	uint32_t            tick_end;
	
	const struct fal_partition *fal_part;
};

static enum rym_code ym2fal_on_begin(struct rym_ctx *ctx, rt_uint8_t *buf, rt_size_t len)
{
	struct fdown_ym2fal_ctx *ym2fal_ctx = (struct fdown_ym2fal_ctx *)ctx;
	
	char *file_name, *file_size;

	/* calculate and store file size */
	file_name = (char *)&buf[0];
	file_size = (char *)&buf[rt_strlen(file_name) + 1];
	ym2fal_ctx->update_file_size = atol(file_size);
	ym2fal_ctx->update_file_pos = 0;
	if (ym2fal_ctx->update_file_size > ym2fal_ctx->fal_part_max)
	{
		return RYM_CODE_CAN;
	}
	LOG_I("size: 0x%08x",  ym2fal_ctx->update_file_size);
	LOG_I("fal erase: 0x%08x 0x%08x", ym2fal_ctx->fal_part_offset, ym2fal_ctx->fal_part_max);
	/* erase DL section */
	if (fal_partition_erase(ym2fal_ctx->fal_part,  ym2fal_ctx->fal_part_offset, ym2fal_ctx->update_file_size) < 0)
	{
		return RYM_CODE_CAN;
	}
	
	ym2fal_ctx->tick_start = rt_tick_get();
	return RYM_CODE_ACK;
}

static enum rym_code ym2fal_on_data(struct rym_ctx *ctx, rt_uint8_t *buf, rt_size_t len)
{
	struct fdown_ym2fal_ctx *ym2fal_ctx = (struct fdown_ym2fal_ctx *)ctx;
	if (fal_partition_write(ym2fal_ctx->fal_part, ym2fal_ctx->fal_part_offset + ym2fal_ctx->update_file_pos, buf, len) < 0)
	{
		return RYM_CODE_CAN;
	}
	ym2fal_ctx->update_file_pos += len;
	return RYM_CODE_ACK;
}


static enum rym_code ym2fal_on_end( struct rym_ctx *ctx, rt_uint8_t *buf, rt_size_t len)
{
	struct fdown_ym2fal_ctx *ym2fal_ctx = (struct fdown_ym2fal_ctx *)ctx;

	ym2fal_ctx->tick_end = rt_tick_get();

	int val = ((ym2fal_ctx->update_file_size * 1000U) /(ym2fal_ctx->tick_end - ym2fal_ctx->tick_start));
	LOG_I("download used: %d ms , average speed: %d bytes/s ",(ym2fal_ctx->tick_end - ym2fal_ctx->tick_start), val );
	return RYM_CODE_ACK;
}



int fdown_ym2fal(const char *device_name, const char *fal_part_name)
{
	struct fdown_ym2fal_ctx *ym2fal_ctx = rt_calloc(1, sizeof(struct fdown_ym2fal_ctx));

	LOG_I("%s(\"%s\", \"%s\");", __FUNCTION__, device_name, fal_part_name);
	
	if (!ym2fal_ctx)
	{
			LOG_I("rt_malloc failed\n");
			return RT_ENOMEM;
	}

	ym2fal_ctx->fal_part = fal_partition_find(fal_part_name);
	if (ym2fal_ctx->fal_part == RT_NULL)
	{
		LOG_E("fal partition (%s) is not exist!", fal_part_name);
		return 0;
	}
	ym2fal_ctx->device = rt_device_find(device_name);
	if (ym2fal_ctx->device == RT_NULL)
	{
		LOG_I("rt device (%s) is not exist!\n", device_name);
		return 0;
	}
	ym2fal_ctx->fal_part_offset = 0;
	if (ym2fal_ctx->fal_part_max <= 0)
	{
		ym2fal_ctx->fal_part_max = ym2fal_ctx->fal_part->len;
	}
	LOG_I("fal range: 0x%08x 0x%08x", ym2fal_ctx->fal_part_offset, ym2fal_ctx->fal_part_max);

	LOG_I("ymodem start!!!");
	if (!rym_recv_on_device(&(ym2fal_ctx->parent), ym2fal_ctx->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX, ym2fal_on_begin, ym2fal_on_data, ym2fal_on_end, RT_TICK_PER_SECOND))
	{
		rt_thread_mdelay(100);
		LOG_I("ymodem end");
	}
	else
	{
		LOG_I("ymodem fail");
	}
	if(ym2fal_ctx)
	{
	  rt_free(ym2fal_ctx);
	}
	return 0;
}





#if (FBOOT_USING_FILESYSTEM == 1)

#ifdef RT_USING_DFS
#include <dfs.h>
#include <dfs_fs.h>
#include <dfs_file.h>
#endif

#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/time.h>
#include <sys/ioctl.h>

struct fdown_ym2fs_ctx
{
	struct rym_ctx      parent;
	
	rt_device_t         device;
	
	char                fpath[DIRENT_NAME_MAX];
	
	int                 fd;
	int                 flen;
	
	uint32_t            tick_start;
	uint32_t            tick_end;
};

static enum rym_code ym2fs_on_begin(struct rym_ctx *ctx, rt_uint8_t *buf, rt_size_t len)
{
	struct fdown_ym2fs_ctx *cctx = (struct fdown_ym2fs_ctx *)ctx;

	cctx->fpath[0] = '/';
	rt_strncpy(&(cctx->fpath[1]), (const char *)buf, len - 1);
	cctx->fd = open(cctx->fpath, O_CREAT | O_WRONLY | O_TRUNC, 0);
	if (cctx->fd < 0)
	{
		rt_err_t err = rt_get_errno();
		rt_kprintf("error creating file: %d\n", err);
		return RYM_CODE_CAN;
	}
	cctx->flen = atoi(1 + (const char *)buf + rt_strnlen((const char *)buf, len - 1));
	if (cctx->flen == 0)
	{
		cctx->flen = -1;
	}
	cctx->tick_start = rt_tick_get();
	return RYM_CODE_ACK;
}

static enum rym_code ym2fs_on_data( struct rym_ctx *ctx, rt_uint8_t *buf, rt_size_t len)
{
	struct fdown_ym2fs_ctx *cctx = (struct fdown_ym2fs_ctx *)ctx;

	RT_ASSERT(cctx->fd >= 0);
	if (cctx->flen == -1)
	{
		write(cctx->fd, buf, len);
	}
	else
	{
		int wlen = len > cctx->flen ? cctx->flen : len;
		write(cctx->fd, buf, wlen);
		cctx->flen -= wlen;
	}
	return RYM_CODE_ACK;
}

static enum rym_code ym2fs_on_end( struct rym_ctx *ctx, rt_uint8_t *buf, rt_size_t len)
{
	struct fdown_ym2fs_ctx *cctx = (struct fdown_ym2fs_ctx *)ctx;

	RT_ASSERT(cctx->fd >= 0);
	close(cctx->fd);
	cctx->fd = -1;

	cctx->tick_end = rt_tick_get();

	int val = ((cctx->flen * 1000U) /(cctx->tick_end - cctx->tick_start));
	LOG_I("download used: %d ms , average speed: %d bytes/s ",(cctx->tick_end - cctx->tick_start), val );
	return RYM_CODE_ACK;
}

int fdown_ym2fs(const char *device_name, const char *file_path)
{
	struct fdown_ym2fs_ctx *ym2fs_ctx = rt_calloc(1, sizeof(struct fdown_ym2fs_ctx));

	LOG_I("%s(\"%s\", \"%s\");", __FUNCTION__, device_name, file_path);
	
	if (!ym2fs_ctx)
	{
		LOG_I("rt_malloc failed\n");
		return RT_ENOMEM;
	}
	ym2fs_ctx->device = rt_device_find(device_name);
	if (ym2fs_ctx->device == RT_NULL)
	{
		LOG_I("rt device (%s) is not exist!\n", device_name);
		return 0;
	}
	LOG_I("ymodem start!!!");
	if (!rym_recv_on_device(&(ym2fs_ctx->parent), ym2fs_ctx->device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX, ym2fs_on_begin, ym2fs_on_data, ym2fs_on_end, RT_TICK_PER_SECOND))
	{
		rt_thread_mdelay(100);
		LOG_I("ymodem end");
	}
	else
	{
		LOG_I("ymodem fail");
	}
	if(ym2fs_ctx)
	{
	  rt_free(ym2fs_ctx);
	}
	return 0;
}

#endif /* FBOOT_USING_FILESYSTEM */


#endif /* FDOWN_USING_YMODEM */

#endif /* FDOWN_USING_FDOWN */
